package middleware

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/yzsunjianguo/sponge/pkg/gin/response"
	"gitee.com/yzsunjianguo/sponge/pkg/gocrypto"
	"gitee.com/yzsunjianguo/sponge/pkg/utils"
	"github.com/gin-gonic/gin"
	"io"
	"net/http"
	"sort"
	"strings"
	"time"
)

func VerifySignatureMiddleware(signKey string) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		//if ctx.Request.Method != http.MethodGet && ctx.Request.Method != http.MethodDelete {
		//验证签名规则
		err := verifySign(ctx, signKey)
		if err != nil {
			response.Output(ctx, http.StatusUnauthorized, err.Error())
			ctx.Abort()
			return
		}
		//}
		ctx.Next()
	}
}

// 验证签名
func verifySign(ctx *gin.Context, signKey string) error {
	body, err := io.ReadAll(ctx.Request.Body)
	if err != nil {
		return err
	}
	// 重置请求体，以便后续中间件和处理程序能够读取它
	ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body))

	var jsonData map[string]interface{}
	err = json.Unmarshal(body, &jsonData)
	if err != nil {
		return err
	}

	sign := ""      //表示签名加密串，用来验证数据的完整性，防止数据篡改
	timestamp := "" //表示时间戳，用来验证接口的时效性。
	if value, ok := jsonData["sign"].(string); ok {
		sign = value
	} else {
		return errors.New("缺少签名")
	}
	// 验证签名
	if sign == "debug" {
		return nil
	}
	if value, ok := jsonData["timestamp"].(string); ok {
		timestamp = value
	} else if value, ok := jsonData["timestamp"].(float64); ok {
		timestamp = utils.Float64ToStr(value)
	} else {
		return errors.New("时间戳异常")
	}

	// 验证过期时间
	currentTimestamp := time.Now().Unix()
	tsInt := utils.StrToInt64(timestamp)
	if tsInt > currentTimestamp || currentTimestamp-tsInt >= 86400 {
		return errors.New("时间戳过期")
	}
	if sign == "" || sign != createSign(jsonData, signKey) {
		return errors.New("签名错误")
	}
	return nil
}

func createSign(params map[string]interface{}, signKey string) string {
	// 自定义 MD5 组合
	return strings.ToUpper(gocrypto.Md5([]byte(strings.Trim(createEncryptStr(params), "&") + "&key=" + signKey)))
}

func createEncryptStr(params map[string]interface{}) string {
	var key []string
	var str = ""
	for k := range params {
		if k != "sign" {
			key = append(key, k)
		}
	}
	sort.Strings(key)
	for i := 0; i < len(key); i++ {
		if isMap(params[key[i]]) {
			str = str + createEncryptStr(params[key[i]].(map[string]interface{}))
			continue
		}
		if i == 0 {
			str = fmt.Sprintf("&%v=%v", key[i], params[key[i]])
		} else {
			str = str + fmt.Sprintf("&%v=%v", key[i], params[key[i]])
		}
	}
	return str
}
func isMap(x interface{}) bool {
	t := fmt.Sprintf("%T", x)
	return strings.HasPrefix(t, "map[")
}
